﻿/*
* Copyright 2004-2010 mb_sx@hotmail.com
* All Rights Reserved.
*/
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Threading;
using System.IO;
using Outlook = Microsoft.Office.Interop.Outlook;
using System.Configuration;
using DeskNotes.Utils;

namespace DeskNotes
{
    class Program : ApplicationContext
    {
        private DeskNotesData deskNotes;
        private MainForm mainForm;

        const string DEBUG_DATA_FILE = "\\Debug_Notes.xml";
        const string DEBUG_ARCHIVE_FILE = "\\Debug_Notes_Archive.xml";


        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            //Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            Thread.CurrentThread.Name = "UI";
            if (args.Length > 0)
            {
                foreach (string arg in args)
                    switch (arg)
                    {
                        case "/debug":
                        case "/DEBUG":
                        case "/Debug":

                            //Pass debug regedit key into desknotes.
                            DeskNotesData.Debug = arg;
                            break;
                        case "/logfile":
                        case "/LOGFILE":
                            Logger.Open(Logger.Output.File);
                            break;
                        case "/console":
                        case "/CONSOLE":
                            Logger.Open(Logger.Output.Console);
                            break;
                        default:
                            Console.WriteLine("DeskNotes accepts following arguments: ");
                            Console.WriteLine("     /debug - to run in debug mode");
                            Console.WriteLine("     /logfile - to write log messages into file: " + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +
                                "\\" + SearchableArchive.DEFAULT_DESKNOTES_FOLDER + "\\" + Logger.logFile);
                            Console.WriteLine("     /console - to write log messages into Console");
                            return;
                        //break;
                    }
            }
            //debug windows information
            Logger.WriteLine("SM_CXBORDER: {0}", WinImports.GetSystemMetrics(WinImports.SM_CXBORDER));
            Logger.WriteLine("SM_CXEDGE: {0}", WinImports.GetSystemMetrics(WinImports.SM_CXEDGE));
            Logger.WriteLine("SM_CXFIXEDFRAME: {0}", WinImports.GetSystemMetrics(WinImports.SM_CXFIXEDFRAME));
            Logger.WriteLine("SM_CXFOCUSBORDER: {0}", WinImports.GetSystemMetrics(WinImports.SM_CXFOCUSBORDER));
            Logger.WriteLine("SM_CXSIZEFRAME: {0}", WinImports.GetSystemMetrics(WinImports.SM_CXSIZEFRAME));

            Application.Run(new Program());
        }

        public Program()
        {
            deskNotes = new DeskNotesData();
            ReadApplicationSettings();
            Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(Properties.Settings.Default.Culture);
            EventHandler appExit = new EventHandler(this.OnApplicationExit);
            Application.ApplicationExit += appExit; //new EventHandler(this.OnApplicationExit);
            //open main form to display icon
            mainForm = new MainForm(deskNotes);
            mainForm.ApplicationExitEventHandler = appExit;
            mainForm.Show();
            //load all desknotes
            OpenDeskNotes();
        }

        private void ReadApplicationSettings()
        {
            Properties.Settings settings = Properties.Settings.Default;

            try
            {
                if ("" == settings.DataFile)
                {   // 1st try to upgrade from previous version of 2.x.x.x DeskNotes
                    //settings.Upgrade();
                    TryGetPreviousUserSettings();
                }
                if ("" == settings.SearchableArchiveUrl)
                {
                    Random rand = new Random();
                    System.Text.StringBuilder index = new System.Text.StringBuilder(300);
                    index.Append(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +
                        "\\" + SearchableArchive.DEFAULT_DESKNOTES_FOLDER + "\\" + SearchableArchive.DEFAULT_SEARCHABLE_ARCHIVE);
                    index.Append(rand.Next().ToString());
                    index.Append(".xml");
                    settings.SearchableArchiveUrl = index.ToString();
                }
                deskNotes.DeleteArchiveIndex();

                if ("" != settings.DataFile)
                {
                    // set extra PageMargins.
                    if (null != settings.PrintMargins)
                        deskNotes.PageSettings.Margins = settings.PrintMargins;

                    //Verify if Outlook is installed & usable
                    try
                    {
                        Outlook.Application olApp = new Outlook.Application();
                        Outlook.NoteItem note = (Outlook.NoteItem)olApp.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olNoteItem);
                        note.Delete();
                        note = null;
                        deskNotes.OutlookOK = true;
                    }
                    catch (Exception e)
                    {
                        deskNotes.OutlookOK = false;
                        Logger.WriteLine(e.Message + "\\n" + e.StackTrace);
                    }
                }
                else
                {   //read settings from previous version if datafile is not defined.
                    deskNotes.oldDataFiles = deskNotes.Read_1_5_Settings();
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(mainForm, MainResource.GetString("ERROR_READ_SETTINGS") + e.Message,
                    MainResource.GetString("APP_TITLE"),
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            { 
               //When Debugging provide extra debugging data files.
#if DEBUG
                string path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                Properties.Settings.Default.DataFile = path + DEBUG_DATA_FILE;
                Properties.Settings.Default.ArchiveFile = path + DEBUG_ARCHIVE_FILE;
#endif
                // remember back color for cases when changed globally from settings dlg.    
                deskNotes.oldBackColor = settings.BackgroundColor;
                //react to certain properties change after initial setup.
                settings.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(deskNotes.settings_PropertyChanged);
            }
        }

        /// <summary>
        /// Loads all desknotes on desktop.
        /// Performs also data files initialization
        /// </summary>
        private void OpenDeskNotes()
           {
			try 
			{
				bool initiated = true;
				switch (deskNotes.UsageStyle){
                    case DeskNotesData.UsageType.FirstTime:
                        // Creates new files, ask for their names.
					    initiated = InitializeDataFile();
                        break;
                    case DeskNotesData.UsageType.OldVersionFound:
                        // Copy old data into new files.
                        initiated = CopyOldDataFiles();
                        break;
                    case DeskNotesData.UsageType.Regular:
                        initiated = true;
                        break;
                }
                
				if (initiated)
				{
                    // Settings should be saved when user specified data files to be used.
                    Properties.Settings.Default.Save(); 
                    deskNotes.OpenDataSet();
                    deskNotes.ReadAllData();
					// call showdesknotes when preprocessing is done
					deskNotes.ShowDeskNotes();
                    deskNotes.CreateArchiveIndex();
                    // Clean up orphans in a data file
                    deskNotes.CleanOrphans();
                }
				else
				{
					Application.Exit();
				}
			} 
			catch (Exception ex)
			{
				//this.Hide();
				MessageBox.Show(mainForm, MainResource.GetString("ERROR_READ_DATA") + ex.Message,
					MainResource.GetString("APP_TITLE"),
					MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
        }

            
		/// <summary>
		/// Method handles user input re data file to be used or created. It is called when Registry does not 
		/// contain DataFile value.
		/// It offers three options:
		/// 1st - create a new file
		/// 2nd - choose an existing one, but missing for some reason
		/// 3rd - cancel application startup and quit app.
		/// </summary>
		/// <returns>true when data file was selected or new created, otherwise false.</returns>
	
		private bool InitializeDataFile()
		{
			try 
			{
                Properties.Settings settings = Properties.Settings.Default;
                SetDataStorageDlg dataStoreDlg = new SetDataStorageDlg();
                dataStoreDlg.storageControl.CheckFileExists = false;
                if ("" == settings.ArchiveFile)
                    dataStoreDlg.storageControl.ArchiveFileName = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\" + DeskNotesData.DEFAULT_ARCHIVE;
                else
                    dataStoreDlg.storageControl.ArchiveFileName = settings.ArchiveFile;
                if ("" == settings.DataFile)
                    dataStoreDlg.storageControl.DataFileName = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\" + DeskNotesData.DEFAULT_DATA;
                else
                    dataStoreDlg.storageControl.DataFileName = settings.DataFile;
                
               
                DialogResult res = dataStoreDlg.ShowDialog(mainForm);

                if (DialogResult.OK == res)
                {

                    // Set archive file name
                    if (dataStoreDlg.storageControl.ArchiveFileName != settings.ArchiveFile)
                    {
                        if (deskNotes.CreateArchiveFile(dataStoreDlg.storageControl.ArchiveFileName))
                            Properties.Settings.Default.ArchiveFile = dataStoreDlg.storageControl.ArchiveFileName;
                        else
                            return false;
                    }

                    // check data file filename
                    if (dataStoreDlg.storageControl.DataFileName != settings.DataFile)
                    {
                        // create new datafile according to entered filename
                        if (deskNotes.CreateNewDataSet(dataStoreDlg.storageControl.DataFileName))
                            settings.DataFile = dataStoreDlg.storageControl.DataFileName;
                        else
                            return false;
                    }
                    else if ("" == dataStoreDlg.storageControl.DataFileName)
                    {  // If data file is not set then it is bad and app cannot be really started.
                        return false;
                    }
                    return true;
                }
                else
                {   // Create default data & archive files and save them in settings. 
                    // Upon next start DN will open Data specified here.
                    Properties.Settings.Default.ArchiveFile = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\" + DeskNotesData.DEFAULT_ARCHIVE;
                    Properties.Settings.Default.DataFile = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\" + DeskNotesData.DEFAULT_DATA;
                    deskNotes.CreateNewDataSet(Properties.Settings.Default.DataFile);
                    Properties.Settings.Default.ShowInitialDeskNote = false;
                    Properties.Settings.Default.Save();
                    return false;
                }
			} 
			catch (Exception ex)
			{// catch any exception which can happen when working with DataSet.
				MessageBox.Show(mainForm, Resources.AppResource.ERROR_INIT_DATA + ex.Message, Resources.AppResource.APP_TITLE,
					MessageBoxButtons.OK, MessageBoxIcon.Error);
				return false;
			} 
		}
        /// <summary>
        /// Asks user to provide new file names where to copy existing old DeskNotes data.
        /// If new names are same as old one then it overwrites them after user confirmation.
        /// </summary>
        /// <returns>True when files were copied or existing files have to be used.</returns>
        private bool CopyOldDataFiles()
        {
            try {
                Properties.Settings settings = Properties.Settings.Default;
                UpgradeDataStorageDlg dlg = new UpgradeDataStorageDlg();
                
                dlg.OldDataFile = settings.DataFile;
                dlg.OldArchiveFile = settings.ArchiveFile;
                dlg.storageControl.CheckFileExists = false;
                dlg.storageControl.OpenDirectory = true;

                if (DialogResult.OK == dlg.ShowDialog(mainForm))
                {
                    //copy files to new one
                    // TODO - more control should be done for existing files and as to overwrite.
                    File.Copy(settings.DataFile, dlg.storageControl.DataFileName, true);
                    settings.DataFile = dlg.storageControl.DataFileName;
                    if ("" != settings.ArchiveFile)
                    {
                        File.Copy(settings.ArchiveFile, dlg.storageControl.ArchiveFileName, true);
                        settings.ArchiveFile = dlg.storageControl.ArchiveFileName;
                    }
                    return true;
                }
                else
                { // Cancel pressed. Then ask if user wants to overwrite existing data files.
                    string text = string.Format(Resources.AppResource.ConfirmOverwriteDataFiles, settings.DataFile, settings.ArchiveFile);
                    if (DialogResult.Yes == MessageBox.Show(mainForm, text, Resources.AppResource.APP_TITLE, 
                        MessageBoxButtons.YesNo, MessageBoxIcon.Question))
                        
                        return true;
                    else
                    {   // User does not want to overwrite existing data files. 
                        // Then it will exit and unset datafiles in config.
                        settings.DataFile = "";
                        settings.ArchiveFile = "";
                        return false;
                    }
                }
            }
            catch (Exception ex)
			{// catch any exception which can happen when working with DataSet.
				MessageBox.Show(mainForm, Resources.AppResource.ERROR_INIT_DATA + ex.Message, Resources.AppResource.APP_TITLE,
					MessageBoxButtons.OK, MessageBoxIcon.Error);
				return false;
			} 
        }

        /// <summary>
        /// Saves all settings and archive and removes tray icon when application exits.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnApplicationExit(object sender, EventArgs e)
        {
            try
            {
                deskNotes.SaveAll();
                RtfConverter.TheBox.Dispose();
                mainForm.ni.Visible = false;
            }
            catch { }
           
        }

        internal void TryGetPreviousUserSettings()
        {
            string thisVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();

           string latestVersion = null;
           if (Program.CopyLatestSettingsDir(Program.GetLatestSettingsDir(out latestVersion)))
            {
                if (latestVersion == thisVersion)
                {
                    Properties.Settings.Default.Reload();
                    Logger.WriteLine("TryGetPreviousUserSettings, same version found, copied and settings Reloaded");
                }
                else
                {
                    Properties.Settings.Default.Upgrade();
                    Logger.WriteLine("TryGetPreviousUserSettings, older version found, copied and settings Upgraded");
                }
            }
            else
            { // nothing was copied, try upgrade
                Properties.Settings.Default.Upgrade();
                Logger.WriteLine("TryGetPreviousUserSettings, nothing was copied, tryed Upgrade");
            }
            
        }
        /// <summary>
        /// Copies the settings folder with latest available user.settings into new location where settings for this app 
        /// </summary>
        /// <param name="settingsFolder">file info for user.settings file to be copied</param>
        /// <returns>true in a case file was copied or it exists in this app location. False if file was not copied or does not exist</returns>
        static public bool CopyLatestSettingsDir(FileSystemInfo settingsFolder)
        {
            // If no previous user.settings exist on disk then return false = no copy done
            if (null == settingsFolder)
                return false;
            // Get the folder for this app and its version and installation settings folder.
            // Described at http://blogs.msdn.com/b/rprabhu/archive/2005/06/29/433979.aspx
            Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal);
            Logger.WriteLine("Local user config path: {0}", config.FilePath);
            string latestSettings = settingsFolder.FullName;
            string currentSettings = config.FilePath;
            if (settingsFolder.Exists)
            {
                // Remove user.config filename from path
                currentSettings = currentSettings.Substring(0, currentSettings.LastIndexOf("\\"));
                // Remove version number dir from path
                currentSettings = currentSettings.Substring(0, currentSettings.LastIndexOf("\\"));
                // Get just the version number               
                string destinationFile = latestSettings.Substring(latestSettings.LastIndexOf("\\") + 1);
                destinationFile = currentSettings + "\\" + destinationFile;
                // create previous version folder under new random folder
                Directory.CreateDirectory(destinationFile);
                destinationFile += "\\user.config";
                // copy previous version user.config file into new folder
                if (!File.Exists(destinationFile))
                {
                    File.Copy(latestSettings + "\\user.config", destinationFile, false);
                    Logger.WriteLine("Settings copied from {0}, to {1}", latestSettings, destinationFile);
                }
                // Return true in a case file was copied or it exists already at destination folder.
                return true;
            }
            return false;
        }

        /// <summary>
        /// Method finds on disk the latest user.settings for this application.
        /// It looks inside Application.LocalUserAppDataPath for directories which follows .NET user settings pattern
        /// <Profile Directory>\<Company Name>\<App Name>_<Evidence Type>_<Evidence Hash>\<Version>\
        /// </summary>
        /// <param name="version">out parameter which contains the version for which settings folder was found</param>
        /// <returns>file info for folder found as the latest available on system</returns>
        static public FileSystemInfo GetLatestSettingsDir(out string version)
        {
            //Get User settings folder for this app. No Roaming settings.           
            string dirName = Application.LocalUserAppDataPath;
            //Get up from <version> folder
            dirName = dirName.Substring(0, dirName.LastIndexOf("\\"));
            //Get up above <appName>.exe_Url_cryptic_string folder. Described why it is cryptic http://blogs.msdn.com/b/rprabhu/archive/2005/06/29/433979.aspx
            dirName = dirName.Substring(0, dirName.LastIndexOf("\\"));
            //Create search string to search for process name. It will search also for vshost process folders
            string searchString = System.Diagnostics.Process.GetCurrentProcess().ProcessName + ".*_Url_*";
            DirectoryInfo di = new DirectoryInfo(dirName);
            //search inside app or company name folder for this app settings with cryptic folder names
            FileSystemInfo[] infos = di.GetFileSystemInfos(searchString);
            // Prepare sorted list to sort by versions of app
            SortedList<System.Version, FileSystemInfo> versionFolders = new SortedList<Version, FileSystemInfo>();
            // Go through all <appName>.exe_Url_cryptic_string folders 
            foreach (FileSystemInfo one in infos)
            {
                //Get subdirs which are version numbers x.x.x.x
                if ((one.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
                {
                    FileSystemInfo[] versions = ((DirectoryInfo)one).GetFileSystemInfos();
                    foreach (FileSystemInfo v in versions)
                    {
                        try
                        {
                            // If version is already in the sorted list then exception is thrown.
                            versionFolders.Add(new Version(v.Name), v);
                        }
                        catch
                        {
                            Logger.WriteLine("Duplicit versions folders found on system: " + v.Name);
                            // compare two user.settings last access time, later modified stays on list
                            FileSystemInfo dirOnList;
                            Version duplicitVersion = new Version(v.Name);
                            if (versionFolders.TryGetValue(duplicitVersion, out dirOnList))
                            {
                                if (dirOnList.LastAccessTimeUtc.CompareTo(v.LastAccessTimeUtc) < 0)
                                {  // folder on the sorted list is earlier than value and will be replaced by one which was accessed more recently
                                    versionFolders.Remove(duplicitVersion);
                                    versionFolders.Add(duplicitVersion, v);
                                }
                            }
                        }
                    }
                }
            }
            //Add this running assembly version with null string as FileSystemInfo
            Version currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
            try
            {   // If there is already user.settings file for this version then it is likely in other cryptic version folder. 
                if (!versionFolders.ContainsKey(currentVersion))
                {   // Add current version to locate latest previous one.
                    versionFolders.Add(currentVersion, null);
                }
                else
                {   // current version is already installed and have user.settings somewhere
                    // return the folder where current version user.settings resides
                    FileSystemInfo thisVersionFolder;
                    if (versionFolders.TryGetValue(currentVersion, out thisVersionFolder))
                    {
                        Logger.WriteLine("Current version settings found at: " + thisVersionFolder.FullName);
                        version = currentVersion.ToString();
                        return thisVersionFolder;
                    }
                }
            }
            catch (Exception e)
            {
                Logger.WriteLine("Exception when adding current version: " + e.StackTrace);
            }
            // Find where is current version in the sorted list.
            int index = versionFolders.IndexOfKey(currentVersion);
            if (index > 0)
            {
                // Get over values in sorted list until we reach current version - 1 index, this is the last but one
                IList<FileSystemInfo> dirs = versionFolders.Values;
                int i = 0;
                foreach (FileSystemInfo fi in dirs)
                {
                    if (i == index - 1)
                    {
                        // Return previous version on disk
                        Logger.WriteLine("Previous version settings are in: " + fi.FullName);
                        version = fi.Name;
                        return fi;
                    }
                    i++;
                }
            }
            version = null;
            return null;
        }
    }
}
